use std::borrow::Cow;

use actix_web::{body::BoxBody, http::header::ContentType, HttpResponse, Responder, ResponseError};
use serde::Serialize;

use crate::error::AppErr;

pub const RESP_HEAD: &'static str = "resp";
pub const RESP_OK: &'static str = "ok";
pub const RESP_FAIL: &'static str = "fail";


#[derive(Debug, Serialize)]
pub struct ErrResp {
    pub err_code: i32,
    pub err_msg: Cow<'static, str>,
}

impl ResponseError for AppErr {

    fn error_response(&self) -> HttpResponse {
        let resp = ErrResp {
            err_code: -1,
            err_msg: Cow::Owned(self.to_string()),
        };
        let body = serde_cbor::to_vec(&resp).unwrap();
        HttpResponse::Ok()
            .insert_header((RESP_HEAD, RESP_FAIL))
            .content_type(ContentType::octet_stream())
            .body(body)
    }
}

#[derive(Debug, Serialize)]
pub struct BodyResp<T>(T);

impl <T: Serialize> Responder for BodyResp<T> {

    type Body = BoxBody;

    fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse {
        let body = serde_cbor::to_vec(&self).unwrap();
        HttpResponse::Ok()
            .insert_header((RESP_HEAD, RESP_OK))
            .content_type(ContentType::octet_stream())
            .body(body)
    }
}

pub type CborResp<T> = Result<BodyResp<T>, AppErr>;


#[inline]
pub fn new_body<T>(body: T) -> Result<BodyResp<T>, AppErr> {
    Ok(BodyResp(body))
}

pub struct VoidResp;

impl Responder for VoidResp {

    type Body = BoxBody;

    fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse {
        HttpResponse::Ok()
            .insert_header((RESP_HEAD, RESP_OK))
            .finish()
    }
}

pub struct DownloadResp {
    name: String,
    body: Vec<u8>,
}

impl DownloadResp {

    pub fn new(name: String, body: Vec<u8>) -> Self {
        DownloadResp { name, body }
    }
}

impl Responder for DownloadResp {
    type Body = BoxBody;

    fn respond_to(self, _req: &actix_web::HttpRequest) -> HttpResponse {
        let name = format!(r#"attachment; filename="{}""#, self.name);
        HttpResponse::Ok()
            .insert_header((RESP_HEAD, RESP_OK))
            .content_type(ContentType::octet_stream())
            .insert_header(("Content-Disposition", name))
            .body(self.body)
    }
}

